#include "comm.h"
#include "linklist.h"
#include "uart.h"
#include "thread.h"

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/tcp.h> 
#include <netinet/ip.h> 
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <sys/select.h>
#include <errno.h>

static linklist_s *serialTonet; //串口转网口
static linklist_s *netToserial; //网口转串口

static volatile int netstatus = 0;  //网络连接状态，0 没有网络连接， 1 有网络连接

//检查tcp连接状态，是否被关闭，无法检测到网络断线
static int SocketConnectStatusJudge(int socket_fd)
{
	int len;
	struct tcp_info info;

	memset(&info, 0, sizeof(struct tcp_info));	
	len = sizeof(info);
	getsockopt(socket_fd, IPPROTO_TCP, TCP_INFO, (void *)&info, (socklen_t *)&len);

	if (TCP_ESTABLISHED == info.tcpi_state) 
	{
		return 0;
	}
	
	return -1;
}


/**--------------------------------------------------------------------------------
* 函数名称 :	SetTcpKeepalive
* 功能描述 :	设置套接字心跳包
* 入口参数 :	int fd    套接字文件描述符
				int start 首次心跳侦测包发送之间的空闲时间    
				int interval 两次心跳侦测包之间的间隔时间   
				int count 探测次数，即将几次探测失败判定为TCP断开  
* 编辑作者 :	
* 返回值   ：   返回0：成功  -1：失败
* 备  注   ：   
---------------------------------------------------------------------------------*/
static int SetTcpKeepalive(int fd, unsigned int start, 
					unsigned int interval, unsigned int count)     
{     
    int keepAlive = 1;     
    if (fd < 0 || start < 0 || interval < 0 || count < 0)
		return -1; 
	
    //启用心跳机制，如果您想关闭，将keepAlive置零即可     
    if(setsockopt(fd,SOL_SOCKET,SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive)) == -1)     
    {     
        perror("setsockopt");     
        return -1;     
    }     
    //启用心跳机制开始到首次心跳侦测包发送之间的空闲时间     
    if(setsockopt(fd,SOL_TCP,TCP_KEEPIDLE,(void *)&start,sizeof(start)) == -1)     
    {     
        perror("setsockopt");     
        return -1;     
    }     
    //两次心跳侦测包之间的间隔时间     
    if(setsockopt(fd,SOL_TCP,TCP_KEEPINTVL,(void *)&interval,sizeof(interval)) == -1)     
    {     
        perror("setsockopt");     
        return -1;     
    }     
    //探测次数，即将几次探测失败判定为TCP断开     
    if(setsockopt(fd,SOL_TCP,TCP_KEEPCNT,(void *)&count,sizeof(count)) == -1)     
    {     
        perror("setsockopt");     
        return -1;     
    }     
    return 0;     
}   





static void delay_ms(unsigned long msec)
{
    int err;
    struct timeval tv;
    tv.tv_sec = msec / 1000;
    tv.tv_usec = (msec % 1000) * 1000;
    
    do
    {
       err = select(0, NULL, NULL, NULL, &tv);
    }
    while (err < 0 && errno == EINTR);

    return ;
}



static int TCPconnect(char *ip, int port)
{
    if((NULL == ip) || (port < 0) || (port > 65535)){
        return -1;
    }
    struct sockaddr_in addr;
    int fd;
    int ret;

    bzero(&addr,sizeof(addr));
	addr.sin_family=AF_INET;
	addr.sin_port=htons(port);
    addr.sin_addr.s_addr=inet_addr(ip); 

    fd = socket(AF_INET, SOCK_STREAM, 0);
	if(fd < 0){
		printf("socket failed \n");
		perror("socket");
		
		return -1;
	}
	printf("socket ok \n");
	
	ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
	if(ret < 0){
		printf("connect failed \n");
		perror("connect");
		return -1;
	}
    SetTcpKeepalive(fd, 1, 1, 1);

    return fd;
}


static int UDPconnect(char *ip, int port)
{
    if((NULL == ip) || (port < 0) || (port > 65535)){
        return -1;
    }
    struct sockaddr_in addr;
    int fd;
    int ret;

    bzero(&addr,sizeof(addr));
	addr.sin_family=AF_INET;
	addr.sin_port=htons(port);
    addr.sin_addr.s_addr=inet_addr(ip); 

    fd = socket(AF_INET, SOCK_DGRAM, 0);
	if(fd < 0){
		zc_debug("socket failed \n");
		perror("socket");
		
		return -1;
	}
	zc_debug("socket ok \n");
	
	ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
	if(ret < 0){
		zc_debug("connect failed \n");
		perror("connect");
		return -1;
	}
    zc_debug("connect to server ok ");
    return fd;
}




//系统初始化
int SysInit(sysConf *sys)
{
    int i = 0;
    char cmd[128] = {0};
    char dev[128] = {0};
    int ret = 0;

    if(!sys){
        return -1;
    }

    memset(sys, 0, sizeof(sysConf));

    if(ParseDevConf(sys) < 0){
        zc_debug("parse file failed ");
        return -1;
    }

    sys->status = SYS_INIT;

    serialTonet = list_init(NULL, sys->crash_size);
    if(NULL == serialTonet){
        return -1;
    }

    netToserial = list_init(NULL, sys->crash_size);
    if(NULL == netToserial){
        list_del(serialTonet);
        return -1;
    }

    for(i=0; i<sys->cnt_net; i++){
        pthread_mutex_init(&(sys->net[i].mutex), NULL);
    }
    

    return 0;
}

//退出系统
void SysExit(sysConf *sys)
{
    int i = 0;

    if(SYS_RUNNING == sys->status){
        zc_debug("sys exit");
        sys->status = SYS_CLOSED;
        //等待其他两个线程退出
        pthread_join(sys->pid_of_net, NULL);
        pthread_join(sys->pid_of_com, NULL);
    }
    

    list_del(netToserial);
    list_del(serialTonet);

}


//网络状态监测线程
void * NetStateThread(void *argc)
{
    sysConf *sys = (sysConf *)argc;
    int i = 0;
    int ret = 0;
    int cnt = 0;
    int flag = 0;

    if(NULL == sys){
        return 0;
    }
    while(1){
        if(SYS_CLOSED == sys->status){
            zc_debug("%s exiting", __func__);
            break;
        }

        flag = 0;
        for(i=0; i<sys->cnt_net; i++){
            if(sys->net[i].enable && (NET_DISCONNECT == sys->net[i].status)){
                if(0 == sys->net[i].counter){
                    //5s重连
                    pthread_mutex_lock(&(sys->net[i].mutex));
                    if(PROTOCAL_TCP == sys->net[i].protocal){
                        ret = TCPconnect(sys->net[i].ip, sys->net[i].port);
                    }else if(PROTOCAL_UDP == sys->net[i].protocal){
                        ret = UDPconnect(sys->net[i].ip, sys->net[i].port);
                    }else{
                        zc_debug("not surpport protocal ");
                        ret = 0;
                    }
                    if(ret > 0){
                        //有网络连接
                        sys->net[i].fd = ret;
                        sys->net[i].status = NET_CONNECTED;
                        sys->net[i].counter = 0;
                        sys->net[i].total_len = 0;
                        zc_debug("connect OK");
                        netstatus = 1;
                    }
                    pthread_mutex_unlock(&(sys->net[i].mutex));
                }
                sys->net[i].counter++;
                if(sys->net[i].counter >= 500){
                    sys->net[i].counter = 0;
                }
            }else if (NET_CONNECTED == sys->net[i].status){
                sys->net[i].counter++;
                flag++;
                netstatus = 1;
                //zc_debug("connected \n");
                if(sys->net[i].counter >= 500){
                    //5s，检测状态
                    pthread_mutex_lock(&(sys->net[i].mutex));
                    ret = SocketConnectStatusJudge(sys->net[i].fd);
                    if(ret < 0){
                        close(sys->net[i].fd);
                        sys->net[i].fd = -1;
                        sys->net[i].status = NET_DISCONNECT;
                        zc_debug("disconnect ");
                    }
                    pthread_mutex_unlock(&(sys->net[i].mutex));
                    sys->net[i].counter = 0;
                }
            }
        }

        if(0 == flag){
            //没有网络连接
            netstatus = 0;
        }
        delay_ms(10);
        

    }

    for(i=0; i<sys->cnt_net; i++){
        if(NET_CONNECTED == sys->net[i].status){
            close(sys->net[i].fd);
        }
    }

    return 0;
}


//网络数据转发线程
void * NetHandleThread(void *argc)
{
    sysConf *sys = (sysConf *)argc;
    if(NULL == sys){
        return 0;
    }

    fd_set rdfds;
    int i = 0;
    int maxfd = 0;
    int flag = 0;
    int ret;
    struct timeval timeout = {0};
    char buf[1024] = {0};
    int len;

    while(1){
        
        if(netstatus && (serialTonet->num > 0)){
            memset(buf, 0, sizeof(buf));
            len = list_pop_node(serialTonet, buf);
            if(len > 0){
                //printf("get serial data[%d] : %s \n", len, buf);
                for(i=0; i<sys->cnt_net; i++){
                    if(NET_CONNECTED != sys->net[i].status){
                        continue;
                    }
                    pthread_mutex_lock(&(sys->net[i].mutex));
                    FD_ZERO(&rdfds);
                    FD_SET(sys->net[i].fd, &rdfds);
                    timeout.tv_sec = 0;
                    timeout.tv_usec = 5000;
                    maxfd = sys->net[i].fd + 1;
                    ret = select(maxfd, NULL, &rdfds, NULL, &timeout);
                    if(ret <= 0){
                        zc_debug("select network fd failed ");
                        //网络可能断开，内核发送队列满
                        close(sys->net[i].fd);
                        sys->net[i].status = NET_DISCONNECT;
                        sys->net[i].counter = 0;
                        pthread_mutex_unlock(&(sys->net[i].mutex));
                        continue;
                    }
                    if(write(sys->net[i].fd, buf, len) < 0){
                        //网络断开
                        perror("write");
                        close(sys->net[i].fd);
                        sys->net[i].status = NET_DISCONNECT;
                        sys->net[i].counter = 0;
                    }
                    pthread_mutex_unlock(&(sys->net[i].mutex));
                }
            }
        }


        FD_ZERO(&rdfds);
        flag = 0;
        for(i=0; i<sys->cnt_net; i++){
            if(NET_CONNECTED == sys->net[i].status){
                FD_SET(sys->net[i].fd, &rdfds); 
                if(0 == flag){
                    maxfd = sys->net[i].fd;
                    flag = 1;
                }else{
                    maxfd = (sys->net[i].fd > maxfd) ? sys->net[i].fd : maxfd;
                }
            }
        }
        if(0 == flag){
            //无连接
            delay_ms(10);
            continue;
        }
        maxfd = maxfd + 1;
        timeout.tv_sec = 0;
        timeout.tv_usec = 5000;
        ret = select(maxfd, &rdfds, NULL, NULL, &timeout);
        if(ret < 0){
            perror("select ");
            break;
        }else if (0 == ret){
            continue;
        }
        for(i=0; i<sys->cnt_net; i++){
            if(NET_CONNECTED == sys->net[i].status){
                if(FD_ISSET(sys->net[i].fd, &rdfds)){
                    //读数据
                    memset(buf, 0, sizeof(buf));
                    pthread_mutex_lock(&(sys->net[i].mutex));
                    ret = read(sys->net[i].fd, buf, sizeof(buf));
                    if(ret <= 0){
                        printf("server close \n");
                        close(sys->net[i].fd);
                        sys->net[i].status = NET_DISCONNECT;
                    }else {
                        sys->net[i].total_len += ret;
                        //printf("buf[total : %d][%d] : %s \n", sys->net[i].total_len, ret, buf);
                        list_add_node(netToserial, buf, ret);
                    }
                    pthread_mutex_unlock(&(sys->net[i].mutex));
                }
            }
        }
        
    }

    SysExit(sys);

    return 0;
}


//串口数据转发线程
void * UartHandThread(void *argc)
{
    sysConf *sys = (sysConf *)argc;
    if(NULL == sys){
        return 0;
    }

    int i = 0;
    char cmd[128] = {0};
    char dev[128] = {0};
    int ret = 0;
    int size = 0;
    char buf[4096] = {0};
    fd_set rdfds;
    int maxfd = 0;
    int flag = 0;
    struct timeval timeout = {0};

    //pthread_detach(pthread_self());

    //串口初始化
    for(i=0; i<sys->cnt_com; i++){
        memset(dev, 0, sizeof(dev));
        sprintf(dev, "/dev/ttyS%d", sys->com[i].num);
        sys->com[i].fd = open(dev, O_RDWR);
        if(sys->com[i].fd < 0){
            zc_debug("open %s failed ", dev);
            exit(0);
        }
        memset(cmd, 0, sizeof(cmd));
        sprintf(cmd, "%d,%d,%d,%c", sys->com[i].baud, sys->com[i].databits,
                sys->com[i].stop, sys->com[i].parity);
        ret = UartInit(sys->com[i].fd, cmd);
        if(ret < 0){
            zc_debug("uart %s init failed [%s]", dev, cmd);
            close(sys->com[i].fd);
            sys->com[i].fd = -1;
            exit(0);
        }
        zc_debug("open %s OK", dev);
        sys->com[i].total_len = 0;
    }

    while(1){
        if(netToserial->num > 0){
            memset(buf, 0, sizeof(buf));
            size = list_pop_node(netToserial, buf);
            //printf("buf[%d] : %s \n", size, buf);
            for(i=0; i<sys->cnt_com; i++){
                UartSend(sys->com[i].fd, buf, size);
            }
        }
        if(SYS_CLOSED == sys->status){
            zc_debug("%s exiting", __func__);
            break;
        }

        FD_ZERO(&rdfds);
        for(i=0; i<sys->cnt_com; i++){
            FD_SET(sys->com[i].fd, &rdfds);
            if(0 == i){
                maxfd = sys->com[i].fd;
            }else{
                maxfd = sys->com[i].fd > maxfd ? sys->com[i].fd : maxfd;
            }
        }
        timeout.tv_sec = 0;
        timeout.tv_usec = 5000;
        maxfd = maxfd + 1;
        ret = select(maxfd, &rdfds, NULL, NULL, &timeout);
        if(ret < 0){
            perror("select uart");
            break;
        }else if (0 == ret){
            continue;
        }

        for(i=0; i<sys->cnt_com; i++){
            if(FD_ISSET(sys->com[i].fd, &rdfds)){
                delay_ms(2);
                memset(buf, 0, sizeof(buf));
                ret = read(sys->com[i].fd, buf, sizeof(buf) - 1);
                if(ret > 0){
                    sys->com[i].total_len += ret;
                    //zc_debug("uart buf[%d][%d] : %s", sys->com[i].total_len, ret, buf);
                    list_add_node(serialTonet, buf, ret);
                }
                    
            }
        }

    }


    for(i=0; i<sys->cnt_com; i++){
        if(sys->com[i].fd > 0){
            close(sys->com[i].fd);
            sys->com[i].fd = -1;
        }
    }

    return 0;
}





int Uart1Test(int num)
{
    int i = 0;
    char cmd[128] = {0};
    char dev[128] = {0};
    int ret = 0;
    char buf[4096] = {0};
    fd_set rdfds;
    int maxfd = 0;
    int fd = -1;
    struct timeval timeout = {0};
    int total = 0;

    if(num < 0){
        num = 200;
    }

    if(num > 4096){
        num = 4096;
    }

    printf("num = %d \n", num);

    //串口初始化
    memset(dev, 0, sizeof(dev));
    sprintf(dev, "/dev/ttyS%d", 1);
    fd = open(dev, O_RDWR);
    if(fd < 0){
        zc_debug("open %s failed ", dev);
        exit(0);
    }
    memset(cmd, 0, sizeof(cmd));
    sprintf(cmd, "%d,%d,%d,%c", 9600, 8,1, 'N');
    ret = UartInit(fd, cmd);
    if(ret < 0){
        zc_debug("uart %s init failed [%s]", dev, cmd);
        close(fd);
        exit(0);
    }
    zc_debug("open %s OK", dev);

    while(1){

        FD_ZERO(&rdfds);
        FD_SET(fd, &rdfds);
        timeout.tv_sec = 0;
        timeout.tv_usec = 5000;
        maxfd = fd + 1;
        ret = select(maxfd, &rdfds, NULL, NULL, &timeout);
        if(ret < 0){
            perror("select uart");
            break;
        }else if (0 == ret){
            continue;
        }

        if(FD_ISSET(fd, &rdfds)){
            delay_ms(2);
            memset(buf, 0, sizeof(buf));
            ret = read( fd, buf, sizeof(buf) - 1);
            if(ret > 0){
                total += ret;
                if(total >= 6){
                    total = 0;
                    memset(buf, 0, sizeof(buf));
                    for(i=0; i<num; i++){
                        buf[i] = i & 0xff;
                    }
                    UartSend(fd, buf, num);
                }
            }
                
        }

    }



    return 0;
}

